﻿using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using MoBaoPay.Model;
using MoBaoPay.Model.Request;
using MoBaoPay.Model.Response;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace MoBaoPay
{
    public class MoBaoPayClient : IMoBaoPayClient
    {
        public const string Name = "MoBaoPay.Client";

        private readonly IHttpClientFactory _httpClientFactory;

        public MoBaoPayClient(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }

        public async Task<MoBaoResponse<MoBaoFundBindCardResponse>> ExecuteBindCardAsync(MoBaoBindCardInput input, MoBaoOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            if (string.IsNullOrEmpty(options.CustNo))
            {
                throw new Exception($"{nameof(options.CustNo)} is empty!");
            }

            //throw new Exception($"{Path.Combine(Directory.GetCurrentDirectory(), options.Path)}");

            var request = new MoBaoBindCardRequest
            {
                CardName = input.CardName,
                CardNo = input.CardNo,
                IdCard = input.IdCard,
                CustName = input.CustName,
                CustNo = options.CustNo,
                PhoneNo = input.PhoneNo
            };

            request.Sign(Path.Combine(Directory.GetCurrentDirectory(), options.Path), options.Password);

            var client = _httpClientFactory.CreateClient(Name);
            var response = await client.PostAsync<MoBaoFundBindCardResponse>(request);
            return response;
        }

        public async Task<MoBaoResponse<MoBaoFundOutResponse>> ExecuteFundOutAsync(MoBaoFundOutInput input, MoBaoOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            if (string.IsNullOrEmpty(options.CustNo))
            {
                throw new Exception($"{nameof(options.CustNo)} is empty!");
            }

            var request = new MoBaoFundOutRequest
            {
                AccInName = input.AccInName,
                AccInNo = input.AccInNo,
                CustNo = options.CustNo,
                MerchTradeNo = input.MerchTradeNo,
                MerchUrl = HttpUtility.UrlEncode(input.MerchUrl),
                TerminalId = input.TerminalId,
                TradeAmt = input.TradeAmt,
                TradeSummer = input.TradeSummer,
            };

            request.Sign(Path.Combine(Directory.GetCurrentDirectory(), options.Path), options.Password);

            var client = _httpClientFactory.CreateClient(Name);
            var response = await client.PostAsync<MoBaoFundOutResponse>(request);
            
            return response;
        }

        public async Task<MoBaoResponse<MoBaoWechatPayResponse>> ExecuteWeChatAsync(MoBaoWeChatPayInput input, MoBaoOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            if (string.IsNullOrEmpty(options.CustNo))
            {
                throw new Exception($"{nameof(options.CustNo)} is empty!");
            }

            var request = new MoBaoWechatPayRequest
            {
                CustNo = options.CustNo,
                MerchTradeNo = input.MerchTradeNo,
                TradeAmt = input.TradeAmt,
                GoodsName = input.GoodsName,
                GoodsDesc = input.GoodsDesc,
                TradeTime = input.TradeTime,
                MerchSyncUrl = input.MerchSyncUrl,
                MerchUrl = input.MerchUrl,
                TradeSummer = input.TradeSummer,
                TerminalId = input.TerminalId,
                OpenId = input.OpenId,
                AppId = input.AppId,
            };

            request.Sign(Path.Combine(Directory.GetCurrentDirectory(), options.Path), options.Password);

            var client = _httpClientFactory.CreateClient(Name);
            var response = await client.PostAsync<MoBaoWechatPayResponse>(request);

            return response;
        }

        public async Task<MoBaoResponse<MoBaoRefundResponse>> ExecuteRefundAsync(MoBaoRefundInput input,
            MoBaoOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            if (string.IsNullOrEmpty(options.CustNo))
            {
                throw new Exception($"{nameof(options.CustNo)} is empty!");
            }

            var request = new MoBaoRefundRequest
            {
                CustNo = options.CustNo,
                MerchTradeNo = input.MerchTradeNo,
                OrderNo = input.OrderNo,
                TerminalId = input.TerminalId,
                TradeAmt = input.TradeAmt
            };
            request.Sign(Path.Combine(Directory.GetCurrentDirectory(), options.Path), options.Password);

            var client = _httpClientFactory.CreateClient(Name);
            var response = await client.PostAsync<MoBaoRefundResponse>(request);

            return response;
        }

        public bool Verify(string strData, string strSign, MoBaoOptions options)
        {
            return MD5WithRSA.Verify(Path.Combine(Directory.GetCurrentDirectory(), options.Path), strData, strSign);
        }

        public MoBaoChatPayDictionary ExecuteAsync(MoBaoWechatPayResponse response)
        {
            if (response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }

            var weChat = JsonConvert.DeserializeObject<WeChatPayCodeUrl>(response.CodeUrl, new JsonSerializerSettings
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver()
            });

            var result = new MoBaoChatPayDictionary
            {
                { "timeStamp", weChat.TimeStamp },
                { "nonceStr", weChat.NonceStr },
                { "package", weChat.Package },
                { "signType", weChat.SignType },
                { "paySign", weChat.PaySign }
            };

            return result;
        }
    }
}